Introduction
In this project we are trying first to explore our data to get a
better understanding To answer our questions we first need to have a
preliminary look at our data, so that we can get a better a idea what we
are dealing with, as well as the possible missing data and relationships
that exist
Preliminary Look at the data
We need first to define the data we have.
| survival |
Survival |
0 = No, 1 = yes |
| pclass |
ticket class |
1 = 1st, 2 = 2nd, 3 = 3rd |
| sex |
sex |
|
| age |
Age in year |
|
| sibsp |
Number of siblings/spouses aboard the titanic |
|
| parch |
Number of parents/children aboard the Titanic |
|
| ticket |
ticket number(unique) |
|
| fare |
Passenger fare |
|
| cabin |
Cabin number |
|
| embarked |
port of embarkation |
C = Cherbourg, Q = Queens-town, S = Southampton |
Loading the packages and the Data
# Loading Packages
## tidyverse loads dplyr and readr
library(tidyverse)
## To have different color maps
library(viridis)
## ggplot2 to produce different plots
library(ggplot2)
## uses ggplot2 to produce a correlation matrix -- the data must be in the correct form
library(ggcorrplot)
## Gives us better themes
library(hrbrthemes)
## to use skewness fun. to calculate skewness of the distribution
library(e1071)
## Multivariate imputation using chained equations -- to impute the missing values in our data
library(mice)
## Loads different statistical functions
library(statsr)
## To produce interactive plot
library(plotly)
# Loading Training Data
train <- read_csv("data/train.csv")
# Loading Testing Data
test <- read_csv("data/test.csv")
# Binding them into a full data frame
df <- bind_rows(train,test)
Exploration Of The Data
Description of the Data
Summary of Data
summary(train)
PassengerId Survived Pclass Name Sex Age
Min. : 1.0 Min. :0.0000 Min. :1.000 Length:891 Length:891 Min. : 0.42
1st Qu.:223.5 1st Qu.:0.0000 1st Qu.:2.000 Class :character Class :character 1st Qu.:20.12
Median :446.0 Median :0.0000 Median :3.000 Mode :character Mode :character Median :28.00
Mean :446.0 Mean :0.3838 Mean :2.309 Mean :29.70
3rd Qu.:668.5 3rd Qu.:1.0000 3rd Qu.:3.000 3rd Qu.:38.00
Max. :891.0 Max. :1.0000 Max. :3.000 Max. :80.00
NA's :177
SibSp Parch Ticket Fare Cabin
Min. :0.000 Min. :0.0000 Length:891 Min. : 0.00 Length:891
1st Qu.:0.000 1st Qu.:0.0000 Class :character 1st Qu.: 7.91 Class :character
Median :0.000 Median :0.0000 Mode :character Median : 14.45 Mode :character
Mean :0.523 Mean :0.3816 Mean : 32.20
3rd Qu.:1.000 3rd Qu.:0.0000 3rd Qu.: 31.00
Max. :8.000 Max. :6.0000 Max. :512.33
Embarked
Length:891
Class :character
Mode :character
Categories of Features
Quantitative data are measures of values or counts and are expressed
as numbers.
Qualitative data are measures of ‘types’ and may be represented by a
name, symbol, or a number code.
Qualitive
Categorical: Survived, Sex, and Embarked. Ordinal: Pclass. Nominal:
Name.
Quantitive
Continuous: Age, Fare. Discrete: SibSp, Parch.
Mix types
Ticket is a mix of numeric and alphanumeric data types Cabin is mix
between alpha and numeric
Exploring Missing Data.
Checking for Missing values in each feature
colSums(is.na(train))
PassengerId Survived Pclass Name Sex Age SibSp Parch
0 0 0 0 0 177 0 0
Ticket Fare Cabin Embarked
0 0 687 2
missing_values <- train %>% summarize_all(funs(sum(is.na(.))/n()))
missing_values <- gather(missing_values, key="feature", value="missing_pct")
missing_values
missing_values %>%
ggplot(aes(x=reorder(feature,-missing_pct),y=missing_pct)) +
geom_bar(stat="identity",fill="red") +
coord_flip() + # to flip the graph
xlab("Features") +
ylab("Percentage of missing values")+
scale_y_continuous(labels=scales::percent) +
theme_ipsum()

Missing data, it is normal?
It is quite normal to see missing data in any data-set as such data
is collected by manually which means that there might be some error.
Missing data present various problems. First, the absence of data
reduces statistical power, which refers to the probability that the test
will reject the null hypothesis when it is false. Second, the lost data
can cause bias in the estimation of parameters. Third, it can reduce the
representativeness of the samples.
The solution to the missing data
Removal
The removal of the observations that contain missing might cause a
bigger issue where it might an even bigger loss of information which
cause bias in our estimation. But on close observation of our data we
can identify that there are some features that are not extremely useful
and contain missing data, so we can drop such features.
train <- train %>%
select(!Cabin)
summary(train)
PassengerId Survived Pclass Name Sex Age
Min. : 1.0 Min. :0.0000 Min. :1.000 Length:891 Length:891 Min. : 0.42
1st Qu.:223.5 1st Qu.:0.0000 1st Qu.:2.000 Class :character Class :character 1st Qu.:20.12
Median :446.0 Median :0.0000 Median :3.000 Mode :character Mode :character Median :28.00
Mean :446.0 Mean :0.3838 Mean :2.309 Mean :29.70
3rd Qu.:668.5 3rd Qu.:1.0000 3rd Qu.:3.000 3rd Qu.:38.00
Max. :891.0 Max. :1.0000 Max. :3.000 Max. :80.00
NA's :177
SibSp Parch Ticket Fare Embarked
Min. :0.000 Min. :0.0000 Length:891 Min. : 0.00 Length:891
1st Qu.:0.000 1st Qu.:0.0000 Class :character 1st Qu.: 7.91 Class :character
Median :0.000 Median :0.0000 Mode :character Median : 14.45 Mode :character
Mean :0.523 Mean :0.3816 Mean : 32.20
3rd Qu.:1.000 3rd Qu.:0.0000 3rd Qu.: 31.00
Max. :8.000 Max. :6.0000 Max. :512.33
Imputation
Imputing the missing data gives the advantage that we use a learning
model to predict such missing data and maintain the distribution of our
data.
Histogram of Age feature.
gAgeDensity <- train %>%
select(Age) %>%
ggplot(aes(Age, y = ..density..)) +
geom_histogram(bins = 20,binwidth = 1,color=inferno(1,alpha=1)) +
geom_density(fill=inferno(1,begin = 0.5,alpha = 0.5),color = inferno(1,begin=0)) +
annotate(
"text",
x = 70,
y = 0.04,
label = paste("Skewness:",skewness(train$Age,na.rm = T)),
colour = inferno(1,begin = 0.1),
size = 4
) +
theme_ipsum_rc()
gAgeDensity

Population mean and standard deviation of Age feature Before
imputation
train %>%
summarise(Age_mean = mean(Age,na.rm = T), Age_sd = sd(Age,na.rm = T))
Histogram of Fare feature
gFareDensity <- train %>%
select(Fare) %>%
ggplot(aes(Fare, y = ..density..)) +
geom_histogram(bins = 20,binwidth = 1,color=viridis(1,alpha=1)) +
geom_density(fill=inferno(1,begin = 0.5,alpha = 0.5),color = viridis(1,begin=0)) +
scale_y_continuous(limits = c(0,0.05))+
theme_ipsum_rc() +
annotate(
"text",
x = 200,
y = 0.05,
label = paste("Skewness",skewness(train$Fare)),
colour = "black",
size = 4
)
gFareDensity

Population mean and standard deviaiton of Fare feature.
train %>%
summarise(Fare_mean = mean(Fare,na.rm = T), Fare_sd = sd(Fare,na.rm = T))
Imputing the missing age feature
#---------------MICE--------------
set.seed(129)
mice_mod <- mice(train[,!names(train) %in% c('PassengerId','Name','Ticket','Cabin','Survived')],method = 'rf')
iter imp variable
1 1 Age
1 2 Age
1 3 Age
1 4 Age
1 5 Age
2 1 Age
2 2 Age
2 3 Age
2 4 Age
2 5 Age
3 1 Age
3 2 Age
3 3 Age
3 4 Age
3 5 Age
4 1 Age
4 2 Age
4 3 Age
4 4 Age
4 5 Age
5 1 Age
5 2 Age
5 3 Age
5 4 Age
5 5 Age
Warning: Number of logged events: 2
mice_output <- complete(mice_mod)
gdistrOriginalData <- train %>%
select(Age) %>%
ggplot(aes(Age, y = ..density..)) +
geom_histogram(bins = 25,binwidth = 1,color=inferno(1,alpha=1)) +
geom_density(fill=inferno(1,begin = 0.5,alpha = 0.5),color = inferno(1,begin=0)) +
ggtitle("Distribution of original data") +
theme_ipsum_rc()
gdistrMICEData <- mice_output %>%
select(Age) %>%
ggplot(aes(Age, y = ..density..)) +
geom_histogram(bins = 25,binwidth = 1,color=inferno(1,alpha=1)) +
geom_density(fill=inferno(1,begin = 0.5,alpha = 0.5),color = inferno(1,begin=0)) +
ggtitle("Distribution of mice output") +
theme_ipsum_rc()
gridExtra::grid.arrange(gdistrOriginalData,gdistrMICEData,nrow = 1)

train$Age <- mice_output$Age
missing_values <- train %>% summarize_all(funs(sum(is.na(.))/n()))
missing_values <- gather(missing_values, key="feature", value="missing_pct")
missing_values
missing_values %>%
ggplot(aes(x=reorder(feature,-missing_pct),y=missing_pct)) +
geom_bar(stat="identity",fill="red") +
coord_flip() + # to flip the graph
xlab("Features") +
ylab("Percentage of missing values")+
scale_y_continuous(labels=scales::percent) +
theme_ipsum()

Population mean and standard deviation
train %>%
summarise(Age_mean = mean(Age,na.rm = T), Age_sd = sd(Age,na.rm = T))
Determining the distrubutuion of Age and Fare By inspection
Though determination of the distribution using inspection is likely
not going to be effective we are going to the KS-test in a later
section
Age
The histogram of the Age feature look very much like a normal
distribution, yet it’s not a normal distribution itself.
Fare
The histogram of the Fare feature fits the chi-square
distribution.
Plotting The Data
We include even more plots of our data to get a better understanding
of it, help us see hidden correlations and ways to facilitate our
analysis
Correlation Matrix
We are going to use correlation matrix of the numerical data to
assess the correlation, which might gives a better idea of which feature
might be important
correlationMatrix <- train %>%
filter(!is.na(Age)) %>%
select(Survived, Pclass,Age,SibSp,Parch,Fare) %>%
cor() %>%
ggcorrplot(lab = T,
ggtheme =theme_ipsum_rc(grid = F),
title="Correlation Matrix",hc.order=T,
colors =rev(viridis(3,alpha=0.7)),
digits = 1)
correlationMatrix

The fare features seems to be the most correlated feature to survival
of the passengers, but it doesn’t negate the importance of the other
features in the data. Which means that we will start by comparing the
each that we consider to be important against survival feature
Class of Passenger Vs Survived
gPclassSurvived <- train %>%
select(Pclass,Survived) %>%
ggplot(aes(as_factor(Pclass),fill=as_factor(Survived))) +
geom_bar(position = "fill") +
scale_y_continuous(labels=scales::percent) +
theme_ipsum_rc() +
labs(x = "Classes",y = "Survival Rate")+
scale_fill_discrete(name = "Survived", labels = c("Didn't Survive","Survived"))
gPclassSurvived

From this Plot, it seems clear that people from the upper classes had
higher survival rates, thought this seemed obvious from the beginning.
### Siblings and Spouses Vs Survived
gSibSpSurvived <- train %>%
select(SibSp,Survived) %>%
ggplot(aes(as_factor(SibSp),fill=as_factor(Survived))) +
geom_bar(position = "fill") +
scale_y_continuous(labels = scales::percent) +
labs(x = "Siblings and Spouses",y = "Survival Rate")+
scale_fill_discrete(name = "Survived", labels = c("Didn't Survive","Survived")) +
theme_ipsum()
gSibSpSurvived

This plot highlight a point that might be counter-intuitive that
people that have no siblings or spouses had lower survival rates than
those with at least one sibling or a spouse
Number of children/parents Vs Survived
gParchSurvived <- train %>%
select(Parch,Survived) %>%
ggplot(aes(as_factor(Parch),fill=as_factor(Survived))) +
geom_bar(position = "fill") +
scale_y_continuous(label = scales::percent)+
labs(x = "Number of parents/children",y = "Survival Rate")+
scale_fill_discrete(name = "Survived", labels = c("Didn't Survive","Survived")) +
theme_ipsum_rc()
gParchSurvived

Gender VS Survived
gSexSurvived <- train %>%
select(Sex,Survived) %>%
ggplot(aes(as_factor(Sex),fill = as_factor(Survived))) +
geom_bar(position = "fill") +
scale_y_continuous(label = scales::percent) +
labs(x = "Sex",y = "Survival Rate")+
scale_fill_discrete(name = "Survived", labels = c("Didn't Survive","Survived")) +
theme_ipsum_rc()
print(gSexSurvived)

Dashboard of the previous graphs
gridExtra::grid.arrange(gPclassSurvived,
gSibSpSurvived,
gSexSurvived,
gParchSurvived,
nrow=2)
train %>%
group_by(Sex) %>%
summarise(Age_mean = mean(Age,na.rm=TRUE),
age_sd = sd(Age,na.rm=T),
surival_mean = mean(Survived,na.rm =T),
surival_sd = sd(Survived,na.rm = T))
Sampling
Random sample of size of 50 (Age)
sample_50 <- sample_n(train,size = 50,replace = T) %>%
summarise(mean = mean(Age), sd = sd(Age))
sample_50
Sampling distrubution with a fixed size (50)
numOfSamples <- seq(50,1000,50)
smplngDstrbtnRpsChng <- tibble()
for(i in numOfSamples){
for(y in 1:i){
nsample <- sample_n(train,size=50,replace=T) %>%
select(Age)
newRow <- nrow(smplngDstrbtnRpsChng) + 1
smplngDstrbtnRpsChng[newRow,"reps"] <- i
smplngDstrbtnRpsChng[newRow,"x_bar"] <- mean(nsample$Age,na.rm = T)
}
}
gSamplingReps <- smplngDstrbtnRpsChng %>%
plot_ly(
x = ~x_bar,
frame = ~reps,
type = "histogram"
)
gSamplingReps
While no. of sample size increase the variability of sampling
distribution decrease and the mean increase.
Sampling distrubution with a fixed repetitions and different
sizes
sizes <- seq(20,260,20)
smplngDstrbtnSzChng <- tibble()
for(i in sizes){
for(y in 1:1500){
nsample <- sample_n(train,size=i,replace=T) %>%
select(Age)
newRow <- nrow(smplngDstrbtnSzChng) + 1
smplngDstrbtnSzChng[newRow,"sizes"] <- i
smplngDstrbtnSzChng[newRow,"x_bar"] <- mean(nsample$Age,na.rm = T)
}
}
gSamplingSize <- smplngDstrbtnSzChng %>%
plot_ly(
x = ~x_bar,
frame = ~sizes,
type = "histogram"
)
gSamplingSize
While no. of sample size increase the variability of sampling
distribution decrease,and The sample distribution mean will be normally
distributed as long as the sample size is more than 30.
kernel distribution
A kernel distribution is a nonparametric representation of the
probability density function (\(pdf\))
of a random variable in any population
The kernel smoothing function defines the shape of the curve used to
generate the pdf Kernel distribution is Quote from histogram in other
word (smooth representation of a histogram) That the integral =1 There
is a benefit of smooth representation of a histogram like Ignores
irregularities and outliers , more efficient in approximation so it
deals better with large data than small data
\[ \hat{f_h} = \frac{1}{n} = \sum^n_{i =
1} K(x-x_i) = \frac{1}{nh} K\left(\frac{x-x_i}{h}\right) \]
Rules
Non-weighted Data
\[ \hat{f_h} = \frac{1}{n} \sum^n_{i = 1}
K(x-x_i) = \frac{1}{nh} K\left(\frac{x-x_i}{h}\right) \]
Weighted Data
\[
\hat{f_h} = \frac{1}{h} \sum^N_{i=1} w_i K \left(\frac{x-x_i}{h}\right),
\qquad \text{where} \sum^N_{i= 1} w_i = 1
\]
Kernel Function
- Box
- Triangle
- Normal
- Pantechnicon
Each density curve uses the same input data, but applies a different
kernel smoothing function to generate the pdf. The density estimates are
roughly comparable, but the shape of each curve varies slightly. For
example, the box kernel produces a density curve that is less smooth
than the others.
The choice of bandwidth value controls the smoothness of the
resulting probability density curve (higher value of \(h\) more smoothing)
Specifying a smaller bandwidth produces a very rough curve, but
reveals that there might be two major peaks in the data. Specifying a
larger bandwidth produces a curve nearly identical to the kernel
function Choosing the optimal (\(h\))
bandwidth methods :
- Silverman’s rule of thump that computes an optimal h by assuming
that data is normally distributed
- Improved Sheather Jones (ISJ) an algorithm is more robust with
multimodality data or a lot of data (one disadvantage is it needs to
large data )
Bounded domains data: have a constrains like data couldn’t be
negative ( -ve lead to probability = 0)
Mirror method
- Mirror the data
- Sum the original and mirrored kernel density estimate
- Chop it so that zero at the boundary side
2D
\(h\) : could be matrix (different
\(h\) in different directions)
The choice of norm comes into \(d \ge
2\)
The p-norm is \(||x||_p := (\sum_{i=1}
|x|^p)^{\frac{1}{p}}\) - norm-p =1 Manhattan distance - norm-p =2
Euclidean norm - norm-p =inf maximum norm (it’s not obvious in every
case which norm is the correct one)
standard euclidean distance is good choice because it invariant under
rotation as large data choice of k and p isn’t important so
Answering our Questions
Acurrate summary of our data.
Q3) i)
ii)?? iii) Yes, by interval estimation iv)?? Q4) Q5)
gAgeDensity <- train %>%
select(Age) %>%
ggplot(aes(Age, y = ..density..)) +
geom_histogram(bins = 20,binwidth = 1,color=inferno(1,alpha=1)) +
geom_density(fill=inferno(1,begin = 0.5,alpha = 0.5),color = inferno(1,begin=0)) +
annotate(
"text",
x = 70,
y = 0.04,
label = paste("Skewness:",skewness(train$Age,na.rm = T)),
colour = inferno(1,begin = 0.1),
size = 4
) +
theme_ipsum_rc()
gAgeDensity
Q6)//?
gFareDensity <- train %>%
select(Fare) %>%
ggplot(aes(Fare, y = ..density..)) +
geom_histogram(bins = 20,binwidth = 1,color=viridis(1,alpha=1)) +
geom_density(fill=inferno(1,begin = 0.5,alpha = 0.5),color = viridis(1,begin=0)) +
scale_y_continuous(limits = c(0,0.05))+
theme_ipsum_rc() +
annotate(
"text",
x = 200,
y = 0.05,
label = paste("Skewness",skewness(train$Fare)),
colour = "black",
size = 4
)
gFareDensity
Q8) Q)9,10,11
numOfSamples <- c(50,100,1000)
smplngDstrbtonRpsChng <- tibble()
for(i in numOfSamples){
for(y in 1:i){
nsample <- sample_n(train,size=50,replace=T) %>%
select(Age)
newRow <- nrow(smplngDstrbtonRpsChng) + 1
smplngDstrbtonRpsChng[newRow,"reps"] <- i
smplngDstrbtonRpsChng[newRow,"x_bar"] <- mean(nsample$Age,na.rm = T)
}
}
gSamplingReps <- smplngDstrbtonRpsChng %>%
plot_ly(
x = ~x_bar,
frame = ~reps,
type = "histogram"
)
gSamplingReps
Q12) Q)13,14,15
Q16) Q17,18)
LS0tDQp0aXRsZTogIlRpdGFuaWMgRGF0YSBBbmFseXNpcyINCmF1dGhvcjogDQogIC0gIkFobWVkIEFzaHJhZiINCiAgLSAiT21hciBHYW1hbCBBYmRlbGF6aXoiDQogIC0gIkFobWVkIFlvdXNyaSINCiAgLSAiQWhtZWQgRGF3b29kIg0KICAtICJBbGlhIE1lZGhhdCINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazoNCiAgICB0b2M6IHllcw0KICBwZGZfZG9jdW1lbnQ6DQogICAgdG9jOiB5ZXMNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIGluY2x1ZGVzOg0KICAgICAgaW5faGVhZGVyOiBwcmVhbWJsZS50ZXgNCiAgICBoaWdobGlnaHQ6IHB5Z21lbnRzDQphbHdheXNfYWxsb3dfaHRtbDogeWVzDQplZGl0b3Jfb3B0aW9uczogDQogIG1hcmtkb3duOiANCiAgICB3cmFwOiA3Mg0KLS0tDQoNCiMgSW50cm9kdWN0aW9uDQoNCkluIHRoaXMgcHJvamVjdCB3ZSBhcmUgdHJ5aW5nIGZpcnN0IHRvIGV4cGxvcmUgb3VyIGRhdGEgdG8gZ2V0IGEgYmV0dGVyDQp1bmRlcnN0YW5kaW5nIFRvIGFuc3dlciBvdXIgcXVlc3Rpb25zIHdlIGZpcnN0IG5lZWQgdG8gaGF2ZSBhDQpwcmVsaW1pbmFyeSBsb29rIGF0IG91ciBkYXRhLCBzbyB0aGF0IHdlIGNhbiBnZXQgYSBiZXR0ZXIgYSBpZGVhIHdoYXQgd2UNCmFyZSBkZWFsaW5nIHdpdGgsIGFzIHdlbGwgYXMgdGhlIHBvc3NpYmxlIG1pc3NpbmcgZGF0YSBhbmQgcmVsYXRpb25zaGlwcw0KdGhhdCBleGlzdA0KDQojIyBQcmVsaW1pbmFyeSBMb29rIGF0IHRoZSBkYXRhDQoNCldlIG5lZWQgZmlyc3QgdG8gZGVmaW5lIHRoZSBkYXRhIHdlIGhhdmUuDQoNCnwgVmFyaWFibGUgfCBEZWZpbml0aW9uICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBLZXkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8Oi0tLS0tLS0tLS0tLS0tLS0tLS18Oi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18Oi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18DQp8IHN1cnZpdmFsIHwgU3Vydml2YWwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgMCA9IE5vLCAxID0geWVzICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBwY2xhc3MgICB8IHRpY2tldCBjbGFzcyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IDEgPSAxc3QsIDIgPSAybmQsIDMgPSAzcmQgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgc2V4ICAgICAgfCBzZXggICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IGFnZSAgICAgIHwgQWdlIGluIHllYXIgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBzaWJzcCAgICB8IE51bWJlciBvZiBzaWJsaW5ncy9zcG91c2VzIGFib2FyZCB0aGUgdGl0YW5pYyB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgcGFyY2ggICAgfCBOdW1iZXIgb2YgcGFyZW50cy9jaGlsZHJlbiBhYm9hcmQgdGhlIFRpdGFuaWMgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IHRpY2tldCAgIHwgdGlja2V0IG51bWJlcih1bmlxdWUpICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBmYXJlICAgICB8IFBhc3NlbmdlciBmYXJlICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgY2FiaW4gICAgfCBDYWJpbiBudW1iZXIgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IGVtYmFya2VkIHwgcG9ydCBvZiBlbWJhcmthdGlvbiAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgQyA9IENoZXJib3VyZywgUSA9IFF1ZWVucy10b3duLCBTID0gU291dGhhbXB0b24gfA0KDQojIyBMb2FkaW5nIHRoZSBwYWNrYWdlcyBhbmQgdGhlIERhdGENCg0KYGBge3IgTG9hZGluZyBQYWNrYWdlcywgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCByZXN1bHRzPSdoaWRlJ30NCiMgTG9hZGluZyBQYWNrYWdlcw0KIyMgdGlkeXZlcnNlIGxvYWRzIGRwbHlyIGFuZCByZWFkcg0KbGlicmFyeSh0aWR5dmVyc2UpDQoNCiMjIFRvIGhhdmUgZGlmZmVyZW50IGNvbG9yIG1hcHMNCmxpYnJhcnkodmlyaWRpcykNCg0KIyMgZ2dwbG90MiB0byBwcm9kdWNlIGRpZmZlcmVudCBwbG90cw0KbGlicmFyeShnZ3Bsb3QyKQ0KDQojIyB1c2VzIGdncGxvdDIgdG8gcHJvZHVjZSBhIGNvcnJlbGF0aW9uIG1hdHJpeCAtLSB0aGUgZGF0YSBtdXN0IGJlIGluIHRoZSBjb3JyZWN0IGZvcm0NCmxpYnJhcnkoZ2djb3JycGxvdCkNCg0KIyMgR2l2ZXMgdXMgYmV0dGVyIHRoZW1lcw0KbGlicmFyeShocmJydGhlbWVzKQ0KDQojIyB0byB1c2Ugc2tld25lc3MgZnVuLiB0byBjYWxjdWxhdGUgc2tld25lc3Mgb2YgdGhlIGRpc3RyaWJ1dGlvbg0KbGlicmFyeShlMTA3MSkNCg0KIyMgTXVsdGl2YXJpYXRlIGltcHV0YXRpb24gdXNpbmcgY2hhaW5lZCBlcXVhdGlvbnMgLS0gdG8gaW1wdXRlIHRoZSBtaXNzaW5nIHZhbHVlcyBpbiBvdXIgZGF0YQ0KbGlicmFyeShtaWNlKQ0KDQojIyBMb2FkcyBkaWZmZXJlbnQgc3RhdGlzdGljYWwgZnVuY3Rpb25zDQpsaWJyYXJ5KHN0YXRzcikNCg0KIyMgVG8gcHJvZHVjZSBpbnRlcmFjdGl2ZSBwbG90DQpsaWJyYXJ5KHBsb3RseSkNCg0KDQojIExvYWRpbmcgVHJhaW5pbmcgRGF0YQ0KdHJhaW4gPC0gcmVhZF9jc3YoImRhdGEvdHJhaW4uY3N2IikNCg0KIyBMb2FkaW5nIFRlc3RpbmcgRGF0YQ0KdGVzdCA8LSByZWFkX2NzdigiZGF0YS90ZXN0LmNzdiIpDQoNCiMgQmluZGluZyB0aGVtIGludG8gYSBmdWxsIGRhdGEgZnJhbWUNCmRmIDwtIGJpbmRfcm93cyh0cmFpbix0ZXN0KQ0KYGBgDQoNCiMgRXhwbG9yYXRpb24gT2YgVGhlIERhdGENCg0KIyMgRGVzY3JpcHRpb24gb2YgdGhlIERhdGENCg0KIyMgU3VtbWFyeSBvZiBEYXRhDQoNCmBgYHtyIERhdGEgU3VtbWFyeX0NCnN1bW1hcnkodHJhaW4pDQpgYGANCg0KIyMgQ2F0ZWdvcmllcyBvZiBGZWF0dXJlcw0KDQo+IFF1YW50aXRhdGl2ZSBkYXRhIGFyZSBtZWFzdXJlcyBvZiB2YWx1ZXMgb3IgY291bnRzIGFuZCBhcmUgZXhwcmVzc2VkDQo+IGFzIG51bWJlcnMuDQoNCj4gUXVhbGl0YXRpdmUgZGF0YSBhcmUgbWVhc3VyZXMgb2YgJ3R5cGVzJyBhbmQgbWF5IGJlIHJlcHJlc2VudGVkIGJ5IGENCj4gbmFtZSwgc3ltYm9sLCBvciBhIG51bWJlciBjb2RlLg0KDQojIyMgUXVhbGl0aXZlDQoNCkNhdGVnb3JpY2FsOiBTdXJ2aXZlZCwgU2V4LCBhbmQgRW1iYXJrZWQuIE9yZGluYWw6IFBjbGFzcy4gTm9taW5hbDoNCk5hbWUuDQoNCiMjIyBRdWFudGl0aXZlDQoNCkNvbnRpbnVvdXM6IEFnZSwgRmFyZS4gRGlzY3JldGU6IFNpYlNwLCBQYXJjaC4NCg0KIyMjIE1peCB0eXBlcw0KDQpUaWNrZXQgaXMgYSBtaXggb2YgbnVtZXJpYyBhbmQgYWxwaGFudW1lcmljIGRhdGEgdHlwZXMgQ2FiaW4gaXMgbWl4DQpiZXR3ZWVuIGFscGhhIGFuZCBudW1lcmljDQoNCiMjIEV4cGxvcmluZyBNaXNzaW5nIERhdGEuDQoNCkNoZWNraW5nIGZvciBNaXNzaW5nIHZhbHVlcyBpbiBlYWNoIGZlYXR1cmUNCg0KYGBge3IgTWlzc2luZyBWYWx1ZXN9DQpjb2xTdW1zKGlzLm5hKHRyYWluKSkNCmBgYA0KDQpgYGB7ciBNaXNzaW5nIHZhbHVlcyBwbG90fQ0KbWlzc2luZ192YWx1ZXMgPC0gdHJhaW4gJT4lIHN1bW1hcml6ZV9hbGwoZnVucyhzdW0oaXMubmEoLikpL24oKSkpDQoNCm1pc3NpbmdfdmFsdWVzIDwtIGdhdGhlcihtaXNzaW5nX3ZhbHVlcywga2V5PSJmZWF0dXJlIiwgdmFsdWU9Im1pc3NpbmdfcGN0IikNCg0KbWlzc2luZ192YWx1ZXMNCg0KbWlzc2luZ192YWx1ZXMgJT4lIA0KICBnZ3Bsb3QoYWVzKHg9cmVvcmRlcihmZWF0dXJlLC1taXNzaW5nX3BjdCkseT1taXNzaW5nX3BjdCkpICsNCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLGZpbGw9InJlZCIpICsNCiAgY29vcmRfZmxpcCgpICsgIyB0byBmbGlwIHRoZSBncmFwaA0KICB4bGFiKCJGZWF0dXJlcyIpICsNCiAgeWxhYigiUGVyY2VudGFnZSBvZiBtaXNzaW5nIHZhbHVlcyIpKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPXNjYWxlczo6cGVyY2VudCkgKw0KICB0aGVtZV9pcHN1bSgpDQpgYGANCg0KIyMjIE1pc3NpbmcgZGF0YSwgaXQgaXMgbm9ybWFsPw0KDQpJdCBpcyBxdWl0ZSBub3JtYWwgdG8gc2VlIG1pc3NpbmcgZGF0YSBpbiBhbnkgZGF0YS1zZXQgYXMgc3VjaCBkYXRhIGlzDQpjb2xsZWN0ZWQgYnkgbWFudWFsbHkgd2hpY2ggbWVhbnMgdGhhdCB0aGVyZSBtaWdodCBiZSBzb21lIGVycm9yLg0KTWlzc2luZyBkYXRhIHByZXNlbnQgdmFyaW91cyBwcm9ibGVtcy4gRmlyc3QsIHRoZSBhYnNlbmNlIG9mIGRhdGENCnJlZHVjZXMgc3RhdGlzdGljYWwgcG93ZXIsIHdoaWNoIHJlZmVycyB0byB0aGUgcHJvYmFiaWxpdHkgdGhhdCB0aGUgdGVzdA0Kd2lsbCByZWplY3QgdGhlIG51bGwgaHlwb3RoZXNpcyB3aGVuIGl0IGlzIGZhbHNlLiBTZWNvbmQsIHRoZSBsb3N0IGRhdGENCmNhbiBjYXVzZSBiaWFzIGluIHRoZSBlc3RpbWF0aW9uIG9mIHBhcmFtZXRlcnMuIFRoaXJkLCBpdCBjYW4gcmVkdWNlIHRoZQ0KcmVwcmVzZW50YXRpdmVuZXNzIG9mIHRoZSBzYW1wbGVzLg0KDQojIyMgVGhlIHNvbHV0aW9uIHRvIHRoZSBtaXNzaW5nIGRhdGENCg0KIyMjIyBSZW1vdmFsDQoNClRoZSByZW1vdmFsIG9mIHRoZSBvYnNlcnZhdGlvbnMgdGhhdCBjb250YWluIG1pc3NpbmcgbWlnaHQgY2F1c2UgYQ0KYmlnZ2VyIGlzc3VlIHdoZXJlIGl0IG1pZ2h0IGFuIGV2ZW4gYmlnZ2VyIGxvc3Mgb2YgaW5mb3JtYXRpb24gd2hpY2gNCmNhdXNlIGJpYXMgaW4gb3VyIGVzdGltYXRpb24uIEJ1dCBvbiBjbG9zZSBvYnNlcnZhdGlvbiBvZiBvdXIgZGF0YSB3ZQ0KY2FuIGlkZW50aWZ5IHRoYXQgdGhlcmUgYXJlIHNvbWUgZmVhdHVyZXMgdGhhdCBhcmUgbm90IGV4dHJlbWVseSB1c2VmdWwNCmFuZCBjb250YWluIG1pc3NpbmcgZGF0YSwgc28gd2UgY2FuIGRyb3Agc3VjaCBmZWF0dXJlcy4NCg0KYGBge3IgZHJvcGluZyBjYWJpbn0NCnRyYWluIDwtIHRyYWluICU+JQ0KICBzZWxlY3QoIUNhYmluKQ0Kc3VtbWFyeSh0cmFpbikNCmBgYA0KDQojIyMjIEltcHV0YXRpb24NCg0KSW1wdXRpbmcgdGhlIG1pc3NpbmcgZGF0YSBnaXZlcyB0aGUgYWR2YW50YWdlIHRoYXQgd2UgdXNlIGEgbGVhcm5pbmcNCm1vZGVsIHRvIHByZWRpY3Qgc3VjaCBtaXNzaW5nIGRhdGEgYW5kIG1haW50YWluIHRoZSBkaXN0cmlidXRpb24gb2Ygb3VyDQpkYXRhLg0KDQojIyBIaXN0b2dyYW0gb2YgQWdlIGZlYXR1cmUuDQoNCmBgYHtyIEFnZSBoaXN0b2dyYW19DQpnQWdlRGVuc2l0eSA8LSB0cmFpbiAlPiUNCiAgc2VsZWN0KEFnZSkgJT4lDQogIGdncGxvdChhZXMoQWdlLCB5ID0gLi5kZW5zaXR5Li4pKSArDQogIGdlb21faGlzdG9ncmFtKGJpbnMgPSAyMCxiaW53aWR0aCA9IDEsY29sb3I9aW5mZXJubygxLGFscGhhPTEpKSArIA0KICBnZW9tX2RlbnNpdHkoZmlsbD1pbmZlcm5vKDEsYmVnaW4gPSAwLjUsYWxwaGEgPSAwLjUpLGNvbG9yID0gaW5mZXJubygxLGJlZ2luPTApKSArIA0KICBhbm5vdGF0ZSgNCiAgICAidGV4dCIsDQogICAgeCA9IDcwLA0KICAgIHkgPSAwLjA0LA0KICAgIGxhYmVsID0gcGFzdGUoIlNrZXduZXNzOiIsc2tld25lc3ModHJhaW4kQWdlLG5hLnJtID0gVCkpLA0KICAgIGNvbG91ciA9IGluZmVybm8oMSxiZWdpbiA9IDAuMSksDQogICAgc2l6ZSA9IDQNCiAgKSArIA0KICB0aGVtZV9pcHN1bV9yYygpDQoNCmdBZ2VEZW5zaXR5DQpgYGANCg0KIyMjIFBvcHVsYXRpb24gbWVhbiBhbmQgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIEFnZSBmZWF0dXJlIEJlZm9yZSBpbXB1dGF0aW9uDQoNCmBgYHtyIFN1bW1hcnkgb2YgQWdlIGZlYXR1cmV9DQp0cmFpbiAlPiUNCiAgc3VtbWFyaXNlKEFnZV9tZWFuID0gbWVhbihBZ2UsbmEucm0gPSBUKSwgQWdlX3NkID0gc2QoQWdlLG5hLnJtID0gVCkpDQpgYGANCg0KIyMgSGlzdG9ncmFtIG9mIEZhcmUgZmVhdHVyZQ0KDQpgYGB7ciBIaXN0b2dyYW0gb2YgRmFyZSBGZWF0dXJlfQ0KZ0ZhcmVEZW5zaXR5IDwtIHRyYWluICU+JQ0KICBzZWxlY3QoRmFyZSkgJT4lDQogIGdncGxvdChhZXMoRmFyZSwgeSA9IC4uZGVuc2l0eS4uKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMjAsYmlud2lkdGggPSAxLGNvbG9yPXZpcmlkaXMoMSxhbHBoYT0xKSkgKyANCiAgZ2VvbV9kZW5zaXR5KGZpbGw9aW5mZXJubygxLGJlZ2luID0gMC41LGFscGhhID0gMC41KSxjb2xvciA9IHZpcmlkaXMoMSxiZWdpbj0wKSkgKyANCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwwLjA1KSkrDQogIHRoZW1lX2lwc3VtX3JjKCkgKyANCiAgYW5ub3RhdGUoDQogICAgInRleHQiLA0KICAgIHggPSAyMDAsDQogICAgeSA9IDAuMDUsDQogICAgbGFiZWwgPSBwYXN0ZSgiU2tld25lc3MiLHNrZXduZXNzKHRyYWluJEZhcmUpKSwNCiAgICBjb2xvdXIgPSAiYmxhY2siLA0KICAgIHNpemUgPSA0DQogICkNCg0KZ0ZhcmVEZW5zaXR5IA0KYGBgDQoNCiMjIyBQb3B1bGF0aW9uIG1lYW4gYW5kIHN0YW5kYXJkIGRldmlhaXRvbiBvZiBGYXJlIGZlYXR1cmUuDQoNCmBgYHtyIFN1bW1hcnkgb2YgRmFyZSBGZWF0dXJlfQ0KdHJhaW4gJT4lDQogIHN1bW1hcmlzZShGYXJlX21lYW4gPSBtZWFuKEZhcmUsbmEucm0gPSBUKSwgRmFyZV9zZCA9IHNkKEZhcmUsbmEucm0gPSBUKSkNCmBgYA0KDQojIyBJbXB1dGluZyB0aGUgbWlzc2luZyBhZ2UgZmVhdHVyZQ0KDQpgYGB7ciBJbXB1dGF0aW9ufQ0KIy0tLS0tLS0tLS0tLS0tLU1JQ0UtLS0tLS0tLS0tLS0tLQ0Kc2V0LnNlZWQoMTI5KQ0KbWljZV9tb2QgPC0gbWljZSh0cmFpblssIW5hbWVzKHRyYWluKSAlaW4lIGMoJ1Bhc3NlbmdlcklkJywnTmFtZScsJ1RpY2tldCcsJ0NhYmluJywnU3Vydml2ZWQnKV0sbWV0aG9kID0gJ3JmJykNCm1pY2Vfb3V0cHV0IDwtIGNvbXBsZXRlKG1pY2VfbW9kKQ0KDQpnZGlzdHJPcmlnaW5hbERhdGEgPC0gdHJhaW4gJT4lDQogIHNlbGVjdChBZ2UpICU+JQ0KICBnZ3Bsb3QoYWVzKEFnZSwgeSA9IC4uZGVuc2l0eS4uKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMjUsYmlud2lkdGggPSAxLGNvbG9yPWluZmVybm8oMSxhbHBoYT0xKSkgKyANCiAgZ2VvbV9kZW5zaXR5KGZpbGw9aW5mZXJubygxLGJlZ2luID0gMC41LGFscGhhID0gMC41KSxjb2xvciA9IGluZmVybm8oMSxiZWdpbj0wKSkgKw0KICBnZ3RpdGxlKCJEaXN0cmlidXRpb24gb2Ygb3JpZ2luYWwgZGF0YSIpICsNCiAgdGhlbWVfaXBzdW1fcmMoKQ0KZ2Rpc3RyTUlDRURhdGEgPC0gbWljZV9vdXRwdXQgJT4lDQogIHNlbGVjdChBZ2UpICU+JQ0KICBnZ3Bsb3QoYWVzKEFnZSwgeSA9IC4uZGVuc2l0eS4uKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMjUsYmlud2lkdGggPSAxLGNvbG9yPWluZmVybm8oMSxhbHBoYT0xKSkgKyANCiAgZ2VvbV9kZW5zaXR5KGZpbGw9aW5mZXJubygxLGJlZ2luID0gMC41LGFscGhhID0gMC41KSxjb2xvciA9IGluZmVybm8oMSxiZWdpbj0wKSkgKyANCiAgZ2d0aXRsZSgiRGlzdHJpYnV0aW9uIG9mIG1pY2Ugb3V0cHV0IikgKw0KICB0aGVtZV9pcHN1bV9yYygpDQoNCmdyaWRFeHRyYTo6Z3JpZC5hcnJhbmdlKGdkaXN0ck9yaWdpbmFsRGF0YSxnZGlzdHJNSUNFRGF0YSxucm93ID0gMSkNCg0KdHJhaW4kQWdlIDwtIG1pY2Vfb3V0cHV0JEFnZQ0KDQptaXNzaW5nX3ZhbHVlcyA8LSB0cmFpbiAlPiUgc3VtbWFyaXplX2FsbChmdW5zKHN1bShpcy5uYSguKSkvbigpKSkNCg0KbWlzc2luZ192YWx1ZXMgPC0gZ2F0aGVyKG1pc3NpbmdfdmFsdWVzLCBrZXk9ImZlYXR1cmUiLCB2YWx1ZT0ibWlzc2luZ19wY3QiKQ0KDQptaXNzaW5nX3ZhbHVlcw0KDQptaXNzaW5nX3ZhbHVlcyAlPiUgDQogIGdncGxvdChhZXMoeD1yZW9yZGVyKGZlYXR1cmUsLW1pc3NpbmdfcGN0KSx5PW1pc3NpbmdfcGN0KSkgKw0KICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsZmlsbD0icmVkIikgKw0KICBjb29yZF9mbGlwKCkgKyAjIHRvIGZsaXAgdGhlIGdyYXBoDQogIHhsYWIoIkZlYXR1cmVzIikgKw0KICB5bGFiKCJQZXJjZW50YWdlIG9mIG1pc3NpbmcgdmFsdWVzIikrDQogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHM9c2NhbGVzOjpwZXJjZW50KSArDQogIHRoZW1lX2lwc3VtKCkNCmBgYA0KDQojIyMgUG9wdWxhdGlvbiBtZWFuIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24NCg0KYGBge3IgU3VtbWFyeSBvZiBBZ2UgZmVhdHVyZSBBZnRlciBJbXB1dGF0aW9ufQ0KdHJhaW4gJT4lDQogIHN1bW1hcmlzZShBZ2VfbWVhbiA9IG1lYW4oQWdlLG5hLnJtID0gVCksIEFnZV9zZCA9IHNkKEFnZSxuYS5ybSA9IFQpKQ0KYGBgDQoNCiMjIERldGVybWluaW5nIHRoZSBkaXN0cnVidXR1aW9uIG9mIEFnZSBhbmQgRmFyZSBCeSBpbnNwZWN0aW9uDQoNClRob3VnaCBkZXRlcm1pbmF0aW9uIG9mIHRoZSBkaXN0cmlidXRpb24gdXNpbmcgaW5zcGVjdGlvbiBpcyBsaWtlbHkgbm90DQpnb2luZyB0byBiZSBlZmZlY3RpdmUgd2UgYXJlIGdvaW5nIHRvIHRoZSBLUy10ZXN0IGluIGEgbGF0ZXIgc2VjdGlvbg0KDQojIyMgQWdlDQoNClRoZSBoaXN0b2dyYW0gb2YgdGhlIEFnZSBmZWF0dXJlIGxvb2sgdmVyeSBtdWNoIGxpa2UgYSBub3JtYWwNCmRpc3RyaWJ1dGlvbiwgeWV0IGl0J3Mgbm90IGEgbm9ybWFsIGRpc3RyaWJ1dGlvbiBpdHNlbGYuDQoNCiMjIyBGYXJlDQoNClRoZSBoaXN0b2dyYW0gb2YgdGhlIEZhcmUgZmVhdHVyZSBmaXRzIHRoZSBjaGktc3F1YXJlIGRpc3RyaWJ1dGlvbi4NCg0KIyMgUGxvdHRpbmcgVGhlIERhdGENCg0KV2UgaW5jbHVkZSBldmVuIG1vcmUgcGxvdHMgb2Ygb3VyIGRhdGEgdG8gZ2V0IGEgYmV0dGVyIHVuZGVyc3RhbmRpbmcgb2YNCml0LCBoZWxwIHVzIHNlZSBoaWRkZW4gY29ycmVsYXRpb25zIGFuZCB3YXlzIHRvIGZhY2lsaXRhdGUgb3VyIGFuYWx5c2lzDQoNCiMjIyBDb3JyZWxhdGlvbiBNYXRyaXgNCg0KV2UgYXJlIGdvaW5nIHRvIHVzZSBjb3JyZWxhdGlvbiBtYXRyaXggb2YgdGhlIG51bWVyaWNhbCBkYXRhIHRvIGFzc2Vzcw0KdGhlIGNvcnJlbGF0aW9uLCB3aGljaCBtaWdodCBnaXZlcyBhIGJldHRlciBpZGVhIG9mIHdoaWNoIGZlYXR1cmUgbWlnaHQNCmJlIGltcG9ydGFudA0KDQpgYGB7ciBDb3JyZWxhdGlvbiBNYXRyaXgsIGZpZy5hbGlnbj0nY2VudGVyJ30NCmNvcnJlbGF0aW9uTWF0cml4IDwtIHRyYWluICU+JQ0KICBmaWx0ZXIoIWlzLm5hKEFnZSkpICU+JQ0KICBzZWxlY3QoU3Vydml2ZWQsIFBjbGFzcyxBZ2UsU2liU3AsUGFyY2gsRmFyZSkgJT4lDQogIGNvcigpICU+JQ0KICBnZ2NvcnJwbG90KGxhYiA9IFQsDQogICAgICAgICAgICAgZ2d0aGVtZSA9dGhlbWVfaXBzdW1fcmMoZ3JpZCA9IEYpLA0KICAgICAgICAgICAgIHRpdGxlPSJDb3JyZWxhdGlvbiBNYXRyaXgiLGhjLm9yZGVyPVQsDQogICAgICAgICAgICAgY29sb3JzID1yZXYodmlyaWRpcygzLGFscGhhPTAuNykpLA0KICAgICAgICAgICAgIGRpZ2l0cyA9IDEpDQoNCmNvcnJlbGF0aW9uTWF0cml4DQpgYGANCg0KVGhlIGZhcmUgZmVhdHVyZXMgc2VlbXMgdG8gYmUgdGhlIG1vc3QgY29ycmVsYXRlZCBmZWF0dXJlIHRvIHN1cnZpdmFsIG9mDQp0aGUgcGFzc2VuZ2VycywgYnV0IGl0IGRvZXNuJ3QgbmVnYXRlIHRoZSBpbXBvcnRhbmNlIG9mIHRoZSBvdGhlcg0KZmVhdHVyZXMgaW4gdGhlIGRhdGEuIFdoaWNoIG1lYW5zIHRoYXQgd2Ugd2lsbCBzdGFydCBieSBjb21wYXJpbmcgdGhlDQplYWNoIHRoYXQgd2UgY29uc2lkZXIgdG8gYmUgaW1wb3J0YW50IGFnYWluc3Qgc3Vydml2YWwgZmVhdHVyZQ0KDQojIyMgQ2xhc3Mgb2YgUGFzc2VuZ2VyIFZzIFN1cnZpdmVkDQoNCmBgYHtyIFBjbGFzc1ZTU3Vydml2ZWQsZmlnLmFsaWduPSdjZW50ZXInfQ0KZ1BjbGFzc1N1cnZpdmVkIDwtIHRyYWluICU+JQ0KICBzZWxlY3QoUGNsYXNzLFN1cnZpdmVkKSAlPiUNCiAgZ2dwbG90KGFlcyhhc19mYWN0b3IoUGNsYXNzKSxmaWxsPWFzX2ZhY3RvcihTdXJ2aXZlZCkpKSArIA0KICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJmaWxsIikgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPXNjYWxlczo6cGVyY2VudCkgKw0KICB0aGVtZV9pcHN1bV9yYygpICsgDQogIGxhYnMoeCA9ICJDbGFzc2VzIix5ID0gIlN1cnZpdmFsIFJhdGUiKSsNCiAgc2NhbGVfZmlsbF9kaXNjcmV0ZShuYW1lID0gIlN1cnZpdmVkIiwgbGFiZWxzID0gYygiRGlkbid0IFN1cnZpdmUiLCJTdXJ2aXZlZCIpKQ0KDQpnUGNsYXNzU3Vydml2ZWQNCmBgYA0KDQpGcm9tIHRoaXMgUGxvdCwgaXQgc2VlbXMgY2xlYXIgdGhhdCBwZW9wbGUgZnJvbSB0aGUgdXBwZXIgY2xhc3NlcyBoYWQgaGlnaGVyIHN1cnZpdmFsIHJhdGVzLCB0aG91Z2h0IHRoaXMgc2VlbWVkIG9idmlvdXMgZnJvbSB0aGUgYmVnaW5uaW5nLg0KIyMjIFNpYmxpbmdzIGFuZCBTcG91c2VzIFZzIFN1cnZpdmVkDQoNCmBgYHtyIFNpYlNwVlNTdXJ2aXZlZCxmaWcuYWxpZ249J2NlbnRlcid9DQpnU2liU3BTdXJ2aXZlZCA8LSB0cmFpbiAlPiUNCiAgc2VsZWN0KFNpYlNwLFN1cnZpdmVkKSAlPiUNCiAgZ2dwbG90KGFlcyhhc19mYWN0b3IoU2liU3ApLGZpbGw9YXNfZmFjdG9yKFN1cnZpdmVkKSkpICsNCiAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZmlsbCIpICsgDQogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnQpICsNCiAgbGFicyh4ID0gIlNpYmxpbmdzIGFuZCBTcG91c2VzIix5ID0gIlN1cnZpdmFsIFJhdGUiKSsNCiAgc2NhbGVfZmlsbF9kaXNjcmV0ZShuYW1lID0gIlN1cnZpdmVkIiwgbGFiZWxzID0gYygiRGlkbid0IFN1cnZpdmUiLCJTdXJ2aXZlZCIpKSArDQogIHRoZW1lX2lwc3VtKCkNCg0KZ1NpYlNwU3Vydml2ZWQNCmBgYA0KDQpUaGlzIHBsb3QgaGlnaGxpZ2h0IGEgcG9pbnQgdGhhdCBtaWdodCBiZSBjb3VudGVyLWludHVpdGl2ZSB0aGF0IHBlb3BsZSB0aGF0IGhhdmUgbm8gc2libGluZ3Mgb3Igc3BvdXNlcyBoYWQgbG93ZXIgc3Vydml2YWwgcmF0ZXMgdGhhbiB0aG9zZSB3aXRoIGF0IGxlYXN0IG9uZSBzaWJsaW5nIG9yIGEgc3BvdXNlDQoNCiMjIyBOdW1iZXIgb2YgY2hpbGRyZW4vcGFyZW50cyBWcyBTdXJ2aXZlZA0KDQpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcid9DQpnUGFyY2hTdXJ2aXZlZCA8LSB0cmFpbiAlPiUNCiAgc2VsZWN0KFBhcmNoLFN1cnZpdmVkKSAlPiUNCiAgZ2dwbG90KGFlcyhhc19mYWN0b3IoUGFyY2gpLGZpbGw9YXNfZmFjdG9yKFN1cnZpdmVkKSkpICsgDQogIGdlb21fYmFyKHBvc2l0aW9uID0gImZpbGwiKSArDQogIHNjYWxlX3lfY29udGludW91cyhsYWJlbCA9IHNjYWxlczo6cGVyY2VudCkrDQogIGxhYnMoeCA9ICJOdW1iZXIgb2YgcGFyZW50cy9jaGlsZHJlbiIseSA9ICJTdXJ2aXZhbCBSYXRlIikrDQogIHNjYWxlX2ZpbGxfZGlzY3JldGUobmFtZSA9ICJTdXJ2aXZlZCIsIGxhYmVscyA9IGMoIkRpZG4ndCBTdXJ2aXZlIiwiU3Vydml2ZWQiKSkgKw0KICB0aGVtZV9pcHN1bV9yYygpDQpnUGFyY2hTdXJ2aXZlZA0KYGBgDQoNCiMjIyBHZW5kZXIgVlMgU3Vydml2ZWQNCg0KYGBge3IgZmlnLmFsaWduPSdjZW50ZXInfQ0KZ1NleFN1cnZpdmVkIDwtIHRyYWluICU+JQ0KICBzZWxlY3QoU2V4LFN1cnZpdmVkKSAlPiUNCiAgZ2dwbG90KGFlcyhhc19mYWN0b3IoU2V4KSxmaWxsID0gYXNfZmFjdG9yKFN1cnZpdmVkKSkpICsgDQogIGdlb21fYmFyKHBvc2l0aW9uID0gImZpbGwiKSArDQogIHNjYWxlX3lfY29udGludW91cyhsYWJlbCA9IHNjYWxlczo6cGVyY2VudCkgKyANCiAgbGFicyh4ID0gIlNleCIseSA9ICJTdXJ2aXZhbCBSYXRlIikrDQogIHNjYWxlX2ZpbGxfZGlzY3JldGUobmFtZSA9ICJTdXJ2aXZlZCIsIGxhYmVscyA9IGMoIkRpZG4ndCBTdXJ2aXZlIiwiU3Vydml2ZWQiKSkgKw0KICB0aGVtZV9pcHN1bV9yYygpDQpwcmludChnU2V4U3Vydml2ZWQpDQpgYGANCg0KIyMjIERhc2hib2FyZCBvZiB0aGUgcHJldmlvdXMgZ3JhcGhzDQoNCmBgYHtyfQ0KZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UoZ1BjbGFzc1N1cnZpdmVkLA0KICAgICAgICAgICAgICAgICAgICAgICAgZ1NpYlNwU3Vydml2ZWQsDQogICAgICAgICAgICAgICAgICAgICAgICBnU2V4U3Vydml2ZWQsDQogICAgICAgICAgICAgICAgICAgICAgICBnUGFyY2hTdXJ2aXZlZCwNCiAgICAgICAgICAgICAgICAgICAgICAgIG5yb3c9MikNCmBgYA0KDQpgYGB7cn0NCnRyYWluICU+JQ0KICBncm91cF9ieShTZXgpICU+JQ0KICBzdW1tYXJpc2UoQWdlX21lYW4gPSBtZWFuKEFnZSxuYS5ybT1UUlVFKSwNCiAgICAgICAgICAgIGFnZV9zZCA9IHNkKEFnZSxuYS5ybT1UKSwNCiAgICAgICAgICAgIHN1cml2YWxfbWVhbiA9IG1lYW4oU3Vydml2ZWQsbmEucm0gPVQpLA0KICAgICAgICAgICAgc3VyaXZhbF9zZCA9IHNkKFN1cnZpdmVkLG5hLnJtID0gVCkpDQoNCmBgYA0KDQojIyBTYW1wbGluZw0KDQojIyMgUmFuZG9tIHNhbXBsZSBvZiBzaXplIG9mIDUwIChBZ2UpDQoNCmBgYHtyIFNhbXBsaW5nLTF9DQpzYW1wbGVfNTAgPC0gc2FtcGxlX24odHJhaW4sc2l6ZSA9IDUwLHJlcGxhY2UgPSBUKSAlPiUNCiAgc3VtbWFyaXNlKG1lYW4gPSBtZWFuKEFnZSksIHNkID0gc2QoQWdlKSkNCnNhbXBsZV81MA0KYGBgDQojIyMgU2FtcGxpbmcgZGlzdHJ1YnV0aW9uIHdpdGggYSBmaXhlZCBzaXplICg1MCkNCmBgYHtyfQ0KbnVtT2ZTYW1wbGVzIDwtIHNlcSg1MCwxMDAwLDUwKQ0KDQpzbXBsbmdEc3RyYnRuUnBzQ2huZyA8LSB0aWJibGUoKQ0KDQpmb3IoaSBpbiBudW1PZlNhbXBsZXMpew0KICANCiAgZm9yKHkgaW4gMTppKXsNCiAgICBuc2FtcGxlIDwtIHNhbXBsZV9uKHRyYWluLHNpemU9NTAscmVwbGFjZT1UKSAlPiUNCiAgICAgIHNlbGVjdChBZ2UpDQogICAgbmV3Um93IDwtIG5yb3coc21wbG5nRHN0cmJ0blJwc0NobmcpICsgMQ0KICAgIHNtcGxuZ0RzdHJidG5ScHNDaG5nW25ld1JvdywicmVwcyJdIDwtIGkNCiAgICBzbXBsbmdEc3RyYnRuUnBzQ2huZ1tuZXdSb3csInhfYmFyIl0gPC0gbWVhbihuc2FtcGxlJEFnZSxuYS5ybSA9IFQpDQogIH0NCiAgDQp9DQoNCmdTYW1wbGluZ1JlcHMgPC0gc21wbG5nRHN0cmJ0blJwc0NobmcgJT4lDQogIHBsb3RfbHkoDQogICAgeCA9IH54X2JhciwNCiAgICBmcmFtZSA9IH5yZXBzLA0KICAgIHR5cGUgPSAiaGlzdG9ncmFtIg0KICApDQpnU2FtcGxpbmdSZXBzDQpgYGANCldoaWxlIG5vLiBvZiBzYW1wbGUgc2l6ZSBpbmNyZWFzZSB0aGUgdmFyaWFiaWxpdHkgb2Ygc2FtcGxpbmcNCmRpc3RyaWJ1dGlvbiBkZWNyZWFzZSBhbmQgdGhlIG1lYW4gaW5jcmVhc2UuDQoNCiMjIyBTYW1wbGluZyBkaXN0cnVidXRpb24gd2l0aCBhIGZpeGVkIHJlcGV0aXRpb25zIGFuZCBkaWZmZXJlbnQgc2l6ZXMNCg0KYGBge3Igc2FtcGxpbmctZGlzdHJ1YnV0aW9uLXNpemVzfQ0Kc2l6ZXMgPC0gc2VxKDIwLDI2MCwyMCkNCnNtcGxuZ0RzdHJidG5TekNobmcgPC0gdGliYmxlKCkNCmZvcihpIGluIHNpemVzKXsNCiAgZm9yKHkgaW4gMToxNTAwKXsNCiAgICBuc2FtcGxlIDwtIHNhbXBsZV9uKHRyYWluLHNpemU9aSxyZXBsYWNlPVQpICU+JQ0KICAgICAgc2VsZWN0KEFnZSkNCiAgICBuZXdSb3cgPC0gbnJvdyhzbXBsbmdEc3RyYnRuU3pDaG5nKSArIDENCiAgICBzbXBsbmdEc3RyYnRuU3pDaG5nW25ld1Jvdywic2l6ZXMiXSA8LSBpDQogICAgc21wbG5nRHN0cmJ0blN6Q2huZ1tuZXdSb3csInhfYmFyIl0gPC0gbWVhbihuc2FtcGxlJEFnZSxuYS5ybSA9IFQpDQogIH0NCn0NCmdTYW1wbGluZ1NpemUgPC0gc21wbG5nRHN0cmJ0blN6Q2huZyAlPiUNCiAgcGxvdF9seSgNCiAgICB4ID0gfnhfYmFyLA0KICAgIGZyYW1lID0gfnNpemVzLA0KICAgIHR5cGUgPSAiaGlzdG9ncmFtIg0KICApDQpnU2FtcGxpbmdTaXplDQpgYGANCg0KV2hpbGUgbm8uIG9mIHNhbXBsZSBzaXplIGluY3JlYXNlIHRoZSB2YXJpYWJpbGl0eSBvZiBzYW1wbGluZw0KZGlzdHJpYnV0aW9uIGRlY3JlYXNlLGFuZCBUaGUgc2FtcGxlIGRpc3RyaWJ1dGlvbiBtZWFuIHdpbGwgYmUgbm9ybWFsbHkNCmRpc3RyaWJ1dGVkIGFzIGxvbmcgYXMgdGhlIHNhbXBsZSBzaXplIGlzIG1vcmUgdGhhbiAzMC4NCg0KIyBrZXJuZWwgZGlzdHJpYnV0aW9uDQoNCj4gQSBrZXJuZWwgZGlzdHJpYnV0aW9uIGlzIGEgbm9ucGFyYW1ldHJpYyByZXByZXNlbnRhdGlvbiBvZiB0aGUNCj4gcHJvYmFiaWxpdHkgZGVuc2l0eSBmdW5jdGlvbiAoJHBkZiQpIG9mIGEgcmFuZG9tIHZhcmlhYmxlIGluIGFueQ0KPiBwb3B1bGF0aW9uDQoNClRoZSBrZXJuZWwgc21vb3RoaW5nIGZ1bmN0aW9uIGRlZmluZXMgdGhlIHNoYXBlIG9mIHRoZSBjdXJ2ZSB1c2VkIHRvDQpnZW5lcmF0ZSB0aGUgcGRmIEtlcm5lbCBkaXN0cmlidXRpb24gaXMgUXVvdGUgZnJvbSBoaXN0b2dyYW0gaW4gb3RoZXINCndvcmQgKHNtb290aCByZXByZXNlbnRhdGlvbiBvZiBhIGhpc3RvZ3JhbSkgVGhhdCB0aGUgaW50ZWdyYWwgPTEgVGhlcmUNCmlzIGEgYmVuZWZpdCBvZiBzbW9vdGggcmVwcmVzZW50YXRpb24gb2YgYSBoaXN0b2dyYW0gbGlrZSBJZ25vcmVzDQppcnJlZ3VsYXJpdGllcyBhbmQgb3V0bGllcnMgLCBtb3JlIGVmZmljaWVudCBpbiBhcHByb3hpbWF0aW9uIHNvIGl0DQpkZWFscyBiZXR0ZXIgd2l0aCBsYXJnZSBkYXRhIHRoYW4gc21hbGwgZGF0YQ0KDQokJCBcaGF0e2ZfaH0gPSBcZnJhY3sxfXtufSA9IFxzdW1ebl97aSA9IDF9IEsoeC14X2kpICA9IFxmcmFjezF9e25ofSBLXGxlZnQoXGZyYWN7eC14X2l9e2h9XHJpZ2h0KSAkJA0KDQojIyBSdWxlcw0KDQojIyMgTm9uLXdlaWdodGVkIERhdGENCg0KJCQgXGhhdHtmX2h9ID0gXGZyYWN7MX17bn0gXHN1bV5uX3tpID0gMX0gSyh4LXhfaSkgID0gXGZyYWN7MX17bmh9IEtcbGVmdChcZnJhY3t4LXhfaX17aH1ccmlnaHQpICQkDQoNCiMjIyBXZWlnaHRlZCBEYXRhDQoNCiQkDQpcaGF0e2ZfaH0gPSBcZnJhY3sxfXtofSBcc3VtXk5fe2k9MX0gd19pIEsgXGxlZnQoXGZyYWN7eC14X2l9e2h9XHJpZ2h0KSwgXHFxdWFkIFx0ZXh0e3doZXJlfSBcc3VtXk5fe2k9IDF9IHdfaSAgPSAxDQokJA0KDQojIyBLZXJuZWwgRnVuY3Rpb24NCg0KMS4gIEJveA0KMi4gIFRyaWFuZ2xlDQozLiAgTm9ybWFsDQo0LiAgUGFudGVjaG5pY29uDQoNCkVhY2ggZGVuc2l0eSBjdXJ2ZSB1c2VzIHRoZSBzYW1lIGlucHV0IGRhdGEsIGJ1dCBhcHBsaWVzIGEgZGlmZmVyZW50DQprZXJuZWwgc21vb3RoaW5nIGZ1bmN0aW9uIHRvIGdlbmVyYXRlIHRoZSBwZGYuIFRoZSBkZW5zaXR5IGVzdGltYXRlcyBhcmUNCnJvdWdobHkgY29tcGFyYWJsZSwgYnV0IHRoZSBzaGFwZSBvZiBlYWNoIGN1cnZlIHZhcmllcyBzbGlnaHRseS4gRm9yDQpleGFtcGxlLCB0aGUgYm94IGtlcm5lbCBwcm9kdWNlcyBhIGRlbnNpdHkgY3VydmUgdGhhdCBpcyBsZXNzIHNtb290aA0KdGhhbiB0aGUgb3RoZXJzLg0KDQpUaGUgY2hvaWNlIG9mIGJhbmR3aWR0aCB2YWx1ZSBjb250cm9scyB0aGUgc21vb3RobmVzcyBvZiB0aGUgcmVzdWx0aW5nDQpwcm9iYWJpbGl0eSBkZW5zaXR5IGN1cnZlIChoaWdoZXIgdmFsdWUgb2YgJGgkIG1vcmUgc21vb3RoaW5nKQ0KDQpTcGVjaWZ5aW5nIGEgc21hbGxlciBiYW5kd2lkdGggcHJvZHVjZXMgYSB2ZXJ5IHJvdWdoIGN1cnZlLCBidXQgcmV2ZWFscw0KdGhhdCB0aGVyZSBtaWdodCBiZSB0d28gbWFqb3IgcGVha3MgaW4gdGhlIGRhdGEuIFNwZWNpZnlpbmcgYSBsYXJnZXINCmJhbmR3aWR0aCBwcm9kdWNlcyBhIGN1cnZlIG5lYXJseSBpZGVudGljYWwgdG8gdGhlIGtlcm5lbCBmdW5jdGlvbg0KQ2hvb3NpbmcgdGhlIG9wdGltYWwgKCRoJCkgYmFuZHdpZHRoIG1ldGhvZHMgOg0KDQoxLiAgU2lsdmVybWFuJ3MgcnVsZSBvZiB0aHVtcCB0aGF0IGNvbXB1dGVzIGFuIG9wdGltYWwgaCBieSBhc3N1bWluZw0KICAgIHRoYXQgZGF0YSBpcyBub3JtYWxseSBkaXN0cmlidXRlZA0KMi4gIEltcHJvdmVkIFNoZWF0aGVyIEpvbmVzIChJU0opIGFuIGFsZ29yaXRobSBpcyBtb3JlIHJvYnVzdCB3aXRoDQogICAgbXVsdGltb2RhbGl0eSBkYXRhIG9yIGEgbG90IG9mIGRhdGEgKG9uZSBkaXNhZHZhbnRhZ2UgaXMgaXQgbmVlZHMgdG8NCiAgICBsYXJnZSBkYXRhICkNCg0KQm91bmRlZCBkb21haW5zIGRhdGE6IGhhdmUgYSBjb25zdHJhaW5zIGxpa2UgZGF0YSBjb3VsZG4ndCBiZSBuZWdhdGl2ZSAoDQotdmUgbGVhZCB0byBwcm9iYWJpbGl0eSA9IDApDQoNCiMjIE1pcnJvciBtZXRob2QNCg0KMS4gIE1pcnJvciB0aGUgZGF0YQ0KMi4gIFN1bSB0aGUgb3JpZ2luYWwgYW5kIG1pcnJvcmVkIGtlcm5lbCBkZW5zaXR5IGVzdGltYXRlDQozLiAgQ2hvcCBpdCBzbyB0aGF0IHplcm8gYXQgdGhlIGJvdW5kYXJ5IHNpZGUNCg0KIyMgMkQNCg0KJGgkIDogY291bGQgYmUgbWF0cml4IChkaWZmZXJlbnQgJGgkIGluIGRpZmZlcmVudCBkaXJlY3Rpb25zKQ0KDQpUaGUgY2hvaWNlIG9mIG5vcm0gY29tZXMgaW50byAkZCBcZ2UgMiQNCg0KVGhlIHAtbm9ybSBpcyAkfHx4fHxfcCA6PSAoXHN1bV97aT0xfSB8eHxecClee1xmcmFjezF9e3B9fSQgLSBub3JtLXAgPTENCk1hbmhhdHRhbiBkaXN0YW5jZSAtIG5vcm0tcCA9MiBFdWNsaWRlYW4gbm9ybSAtIG5vcm0tcCA9aW5mIG1heGltdW0gbm9ybQ0KKGl0J3Mgbm90IG9idmlvdXMgaW4gZXZlcnkgY2FzZSB3aGljaCBub3JtIGlzIHRoZSBjb3JyZWN0IG9uZSkNCg0Kc3RhbmRhcmQgZXVjbGlkZWFuIGRpc3RhbmNlIGlzIGdvb2QgY2hvaWNlIGJlY2F1c2UgaXQgaW52YXJpYW50IHVuZGVyDQpyb3RhdGlvbiBhcyBsYXJnZSBkYXRhIGNob2ljZSBvZiBrIGFuZCBwIGlzbid0IGltcG9ydGFudCBzbw0KDQojIEFuc3dlcmluZyBvdXIgUXVlc3Rpb25zDQoNCiMjIEFjdXJyYXRlIHN1bW1hcnkgb2Ygb3VyIGRhdGEuDQoNClEzKSBpKQ0KDQppaSk/PyBpaWkpIFllcywgYnkgaW50ZXJ2YWwgZXN0aW1hdGlvbiBpdik/PyBRNCkgUTUpDQoNCmBgYHtyfQ0KZ0FnZURlbnNpdHkgPC0gdHJhaW4gJT4lDQogIHNlbGVjdChBZ2UpICU+JQ0KICBnZ3Bsb3QoYWVzKEFnZSwgeSA9IC4uZGVuc2l0eS4uKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMjAsYmlud2lkdGggPSAxLGNvbG9yPWluZmVybm8oMSxhbHBoYT0xKSkgKyANCiAgZ2VvbV9kZW5zaXR5KGZpbGw9aW5mZXJubygxLGJlZ2luID0gMC41LGFscGhhID0gMC41KSxjb2xvciA9IGluZmVybm8oMSxiZWdpbj0wKSkgKyANCiAgYW5ub3RhdGUoDQogICAgInRleHQiLA0KICAgIHggPSA3MCwNCiAgICB5ID0gMC4wNCwNCiAgICBsYWJlbCA9IHBhc3RlKCJTa2V3bmVzczoiLHNrZXduZXNzKHRyYWluJEFnZSxuYS5ybSA9IFQpKSwNCiAgICBjb2xvdXIgPSBpbmZlcm5vKDEsYmVnaW4gPSAwLjEpLA0KICAgIHNpemUgPSA0DQogICkgKyANCiAgdGhlbWVfaXBzdW1fcmMoKQ0KDQpnQWdlRGVuc2l0eQ0KYGBgDQoNClE2KS8vPw0KDQpgYGB7cn0NCmdGYXJlRGVuc2l0eSA8LSB0cmFpbiAlPiUNCiAgc2VsZWN0KEZhcmUpICU+JQ0KICBnZ3Bsb3QoYWVzKEZhcmUsIHkgPSAuLmRlbnNpdHkuLikpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDIwLGJpbndpZHRoID0gMSxjb2xvcj12aXJpZGlzKDEsYWxwaGE9MSkpICsgDQogIGdlb21fZGVuc2l0eShmaWxsPWluZmVybm8oMSxiZWdpbiA9IDAuNSxhbHBoYSA9IDAuNSksY29sb3IgPSB2aXJpZGlzKDEsYmVnaW49MCkpICsgDQogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsMC4wNSkpKw0KICB0aGVtZV9pcHN1bV9yYygpICsgDQogIGFubm90YXRlKA0KICAgICJ0ZXh0IiwNCiAgICB4ID0gMjAwLA0KICAgIHkgPSAwLjA1LA0KICAgIGxhYmVsID0gcGFzdGUoIlNrZXduZXNzIixza2V3bmVzcyh0cmFpbiRGYXJlKSksDQogICAgY29sb3VyID0gImJsYWNrIiwNCiAgICBzaXplID0gNA0KICApDQoNCmdGYXJlRGVuc2l0eQ0KYGBgDQoNClE4KSBRKTksMTAsMTENCg0KYGBge3J9DQpudW1PZlNhbXBsZXMgPC0gYyg1MCwxMDAsMTAwMCkNCnNtcGxuZ0RzdHJidG9uUnBzQ2huZyA8LSB0aWJibGUoKQ0KZm9yKGkgaW4gbnVtT2ZTYW1wbGVzKXsNCiAgZm9yKHkgaW4gMTppKXsNCiAgICBuc2FtcGxlIDwtIHNhbXBsZV9uKHRyYWluLHNpemU9NTAscmVwbGFjZT1UKSAlPiUNCiAgICAgIHNlbGVjdChBZ2UpDQogICAgbmV3Um93IDwtIG5yb3coc21wbG5nRHN0cmJ0b25ScHNDaG5nKSArIDENCiAgICBzbXBsbmdEc3RyYnRvblJwc0NobmdbbmV3Um93LCJyZXBzIl0gPC0gaQ0KICAgIHNtcGxuZ0RzdHJidG9uUnBzQ2huZ1tuZXdSb3csInhfYmFyIl0gPC0gbWVhbihuc2FtcGxlJEFnZSxuYS5ybSA9IFQpDQogIH0NCn0NCg0KZ1NhbXBsaW5nUmVwcyA8LSBzbXBsbmdEc3RyYnRvblJwc0NobmcgJT4lDQogIHBsb3RfbHkoDQogICAgeCA9IH54X2JhciwNCiAgICBmcmFtZSA9IH5yZXBzLA0KICAgIHR5cGUgPSAiaGlzdG9ncmFtIg0KICApDQoNCmdTYW1wbGluZ1JlcHMNCmBgYA0KDQpRMTIpICBRKTEzLDE0LDE1DQoNCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9DQpzaXplcyA8LSBzZXEoMjAsMjAwLDIwKQ0Kc21wbG5nRHN0cmJ0b25TekNobmcgPC0gdGliYmxlKCkNCmZvcihpIGluIHNpemVzKXsNCiAgZm9yKHkgaW4gMToxNTAwKXsNCiAgICBuc2FtcGxlIDwtIHNhbXBsZV9uKHRyYWluLHNpemU9aSxyZXBsYWNlPVQpICU+JQ0KICAgICAgc2VsZWN0KEFnZSkNCiAgICBuZXdSb3cgPC0gbnJvdyhzbXBsbmdEc3RyYnRvblN6Q2huZykgKyAxDQogICAgc21wbG5nRHN0cmJ0b25TekNobmdbbmV3Um93LCJzaXplcyJdIDwtIGkNCiAgICBzbXBsbmdEc3RyYnRvblN6Q2huZ1tuZXdSb3csInhfYmFyIl0gPC0gbWVhbihuc2FtcGxlJEFnZSxuYS5ybSA9IFQpDQogIH0NCn0NCmdTYW1wbGluZ1NpemUgPC0gc21wbG5nRHN0cmJ0b25TekNobmcgJT4lDQogIHBsb3RfbHkoDQogICAgeCA9IH54X2JhciwNCiAgICBmcmFtZSA9IH5zaXplcywNCiAgICB0eXBlID0gImhpc3RvZ3JhbSINCiAgKQ0KDQpnU2FtcGxpbmdTaXplDQpgYGANCg0KUTE2KSBRMTcsMTgpDQoNCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9DQpzaXplcyA8LSBzZXEoMiw1MCwyKQ0Kc21wbG5nRHN0cmJ0b25TekNobmcgPC0gdGliYmxlKCkNCmZvcihpIGluIHNpemVzKXsNCiAgZm9yKHkgaW4gMToxNTAwKXsNCiAgICBuc2FtcGxlIDwtIHNhbXBsZV9uKHRyYWluLHNpemU9aSxyZXBsYWNlPVQpICU+JQ0KICAgICAgc2VsZWN0KEFnZSkNCiAgICBuZXdSb3cgPC0gbnJvdyhzbXBsbmdEc3RyYnRvblN6Q2huZykgKyAxDQogICAgc21wbG5nRHN0cmJ0b25TekNobmdbbmV3Um93LCJzaXplcyJdIDwtIGkNCiAgICBzbXBsbmdEc3RyYnRvblN6Q2huZ1tuZXdSb3csInZhcmlhbmNlIl0gPC0gc2QobnNhbXBsZSRBZ2UsbmEucm0gPSBUKSoqMg0KICB9DQp9DQpnU2FtcGxpbmdTaXplIDwtIHNtcGxuZ0RzdHJidG9uU3pDaG5nICU+JQ0KICBwbG90X2x5KA0KICAgIHggPSB+dmFyaWFuY2UsDQogICAgZnJhbWUgPSB+c2l6ZXMsDQogICAgdHlwZSA9ICJoaXN0b2dyYW0iDQogICkNCg0KZ1NhbXBsaW5nU2l6ZQ0KYGBgDQo=